package edu.ncsu.csc.ip.common.serializer;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import edu.ncsu.csc.ip.server.ClientBean;

/**
 * Class <code></code>
 * 
 * 
 * 
 * @author Moin Ayazifar <mayazif@ncsu.edu>
 * @author Steven Elliott <sdelliot@ncsu.edu>
 * 
 */

public final class BeanSerializer<T> {

	final Class<T> typeParameterClass;

	private final static String BEAN_DELIMITER = "&";
	private final static String KEY_VALUE_DELIMITER = "=";
	private final static String FIELD_DELIMITER = ",";

	SimpleDateFormat dateFormat = new SimpleDateFormat(
			"EEE MMM dd HH:mm:ss zzz yyyy");

	public BeanSerializer(Class<T> typeParameterClass) {
		this.typeParameterClass = typeParameterClass;
	}

	private final void setPropertyValue(Object javaBean, String propertyName,
			Object propertyValue) {
		try {
			BeanInfo bi = Introspector.getBeanInfo(javaBean.getClass());
			PropertyDescriptor pds[] = bi.getPropertyDescriptors();
			for (PropertyDescriptor pd : pds) {
				if (pd.getName().equals(propertyName)) {
					Method setter = pd.getWriteMethod();
					if (setter != null) {
						setter.invoke(javaBean, new Object[] { propertyValue });
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private final Object getPropertyValue(Object javaBean, String propertyName) {
		Object object = null;
		try {
			BeanInfo bi = Introspector.getBeanInfo(javaBean.getClass());
			PropertyDescriptor pds[] = bi.getPropertyDescriptors();
			for (PropertyDescriptor pd : pds) {
				if (pd.getName().equals(propertyName)) {
					Method getter = pd.getReadMethod();
					if (getter != null) {
						object = getter.invoke(javaBean);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return object;
	}

	public final String getString(List<T> beansList)
			throws IllegalArgumentException, IllegalAccessException {
		StringBuilder sb = new StringBuilder();
		for (T bean : beansList) {
			sb.append(getString(bean));
			sb.append(BEAN_DELIMITER);
		}
		return sb.toString();

	}

	public final String getString(T bean) {

		StringBuilder sb = new StringBuilder();
		Object returnValue;
		for (Field f : bean.getClass().getDeclaredFields()) {
			if (f.isAnnotationPresent(Transfer.class)
					&& (returnValue = getPropertyValue(bean, f.getName())) != null) {
				sb.append(f.getName() + KEY_VALUE_DELIMITER + returnValue
						+ FIELD_DELIMITER);
			}
		}
		return sb.toString();
	}

	public final List<T> getAllBean(String allBeansString)
			throws NumberFormatException, ParseException {
		List<T> beansList = new ArrayList<T>();
		String[] beansStringArray = allBeansString.split(BEAN_DELIMITER);
		for (String beanString : beansStringArray)
			beansList.add(getBean(beanString));
		return beansList;
	}

	public final T getBean(String string) {

		T t = null;

		try {
			t = typeParameterClass.newInstance();

			String allFieldValues[] = string.split(FIELD_DELIMITER);
			HashMap<String, String> fieldValueMap = new HashMap<String, String>();

			String[] fieldValue;
			for (String fieldEqValue : allFieldValues) {
				fieldValue = fieldEqValue.split(KEY_VALUE_DELIMITER);
				fieldValueMap.put(fieldValue[0], fieldValue[1]);
			}

			for (Field f : typeParameterClass.getDeclaredFields()) {
				if (f.isAnnotationPresent(Transfer.class)
						&& fieldValueMap.containsKey(f.getName())) {

					Object value = (Integer.class == f.getType()) ? Integer
							.parseInt(fieldValueMap.get(f.getName()))
							: (Boolean.class == f.getType()) ? Boolean
									.parseBoolean(fieldValueMap.get(f.getName()))
									: (Double.class == f.getType()) ? Double
											.parseDouble(fieldValueMap.get(f
													.getName()))
											: (Date.class == f.getType()) ? dateFormat
													.parse(fieldValueMap.get(f
															.getName()))
													: fieldValueMap.get(f
															.getName());
					setPropertyValue(t, f.getName(), value);
				}

			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return t;
	}

	public static void main(String[] args) throws IllegalArgumentException,
			IllegalAccessException, ParseException {

		BeanSerializer<ClientBean> mapper = new BeanSerializer<ClientBean>(
				ClientBean.class);

		List<ClientBean> beansList = new ArrayList<ClientBean>();

		ClientBean cb = new ClientBean();
		cb.setCookie(1234);
		cb.setHostname("www.google.com");
		cb.setActiveFlag(true);
		cb.setLastAccessDate(new Date());

		ClientBean cb2 = new ClientBean();
		cb2.setCookie(3245678);
		cb2.setHostname("www.yahoo.com");
		cb2.setTtl(121212);
		cb2.setLastAccessDate(mapper.dateFormat
				.parse("Thu Sep 11 12:01:50 EDT 2012"));

		beansList.add(cb);
		beansList.add(cb2);

		System.out.println(">>> " + mapper.getString(beansList));
		//
		System.out.println(">>> "
				+ mapper.getString(mapper.getAllBean(mapper
						.getString(beansList))));

	}
}
